#include "KLineData.h"
#include<algorithm>
#include <sstream>
#include "his_stream.h"
#include "LogSubject.h"

#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
using namespace  RAPIDJSON_NAMESPACE;


CKLineData::CKLineData()
	: nMaxNum(0), nExpired(0), nPort(0), nStatus(1), nPositonTag(0)
	, nRecordNum(0), nCollectTime(0)

{

}

CKLineData::CKLineData(unsigned int maxnum, unsigned int expired, std::string code, std::string sign, std::string ip, unsigned int port)
	: nMaxNum(maxnum), nExpired(expired), nPort(port), nStatus(1), nPositonTag(0)
	, strCode(code),strSign(sign), strIP(ip), nRecordNum(0), nCollectTime(0)
{

}

CKLineData::CKLineData(const CKLineData& other)
{
	nMaxNum      = other.nMaxNum      ;
	nCollectTime = other.nCollectTime ;
	nCurTime     = other.nCurTime     ;
	nExpired     = other.nExpired     ;
	strSign      = other.strSign      ;
	strIP        = other.strIP        ;
	nPort        = other.nPort        ;
	nStatus      = other.nStatus      ;
	strCode      = other.strCode      ;
	nPositonTag  = other.nPositonTag  ;
	nRecordNum   = other.nRecordNum   ;
	vctData      = other.vctData      ;
}


CKLineData& CKLineData::operator=(const CKLineData& other)
{
	if (this == &other)
		return *this;
	
	nMaxNum       = other.nMaxNum     ;   
	nCollectTime  = other.nCollectTime;   
	nCurTime      = other.nCurTime    ;   
	nExpired      = other.nExpired    ;   
	strSign       = other.strSign     ;   
	strIP         = other.strIP       ;   
	nPort         = other.nPort       ;   
	nStatus       = other.nStatus     ;   
	strCode       = other.strCode     ;   
	nPositonTag   = other.nPositonTag ;   
	nRecordNum    = other.nRecordNum  ;   
	vctData		  = other.vctData	  ;
	return *this;
}

CKLineData::~CKLineData()
{

}
bool CKLineData::Update(ModualMsg* msg)
{
	if (!msg) 
		return false;

	if (msg->nStatus == 1)
	{
		vctData.clear();
		nRecordNum = 0;
	}
	else
	{
		Document doc;
		if (doc.Parse(msg->szText.c_str()).HasParseError())
			return false;
		int flag(0),num(0);
		Value::ConstMemberIterator itFind = doc.FindMember("flag");
		if (itFind == doc.MemberEnd() || !itFind->value.IsInt())
			return false;
		flag = itFind->value.GetInt();

		itFind = doc.FindMember("num");
		if (itFind == doc.MemberEnd() || !itFind->value.IsInt())
			return false;
		num = itFind->value.GetInt();

		itFind = doc.FindMember("data");
		if (itFind == doc.MemberEnd() || !itFind->value.IsString())
			return false;
		const char * strTmp = itFind->value.GetString();

		int sizeTotal = itFind->value.GetStringLength();
		int sizeOne = sizeof(SrcKLineData);

		if (nPositonTag == 0)
			sizeOne -= sizeof(int);

		if (sizeTotal < num*sizeOne)
			return false;


		nPositonTag = flag;
		nStatus = msg->nStatus;

		nCurTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
		nCollectTime = msg->nTime;

		CMemStream stream;
		stream.Attach((void *)strTmp, sizeTotal);

		nRecordNum = 0;
		vctData.resize(num);
		int date(0);
		for (int i = 0; i < num; i++)
		{
			stream.ReadInt32(&date);
			if (date > 99999999)
			{
				vctData[i].SetDate(date);
			}
			else
				vctData[i].date = date;
			
			stream.ReadInt32(&vctData[i].open);
			stream.ReadInt32(&vctData[i].high);
			stream.ReadInt32(&vctData[i].low);
			stream.ReadInt32(&vctData[i].close);
			stream.ReadInt32(&vctData[i].volume);
			stream.ReadInt32(&vctData[i].amount);

			vctData[i].position = 0;
			if (nPositonTag == 1)
				stream.ReadInt32(&vctData[i].position);
		}
		nRecordNum = std::min<unsigned int>(nMaxNum, num);


		std::sort(vctData.begin(), vctData.end(), [](KLineData &first, KLineData &second){ return first.date > second.date; });
	}
}

bool CKLineData::CheckExpired(int time)
{
	return nCurTime + nExpired <= time;
}

bool CKLineData::CheckValid()
{
	return nStatus == 0;
}

size_t CKLineData::GetHashCode(bool bTrade /*= false*/)
{

	size_t nHashCode = 0;

	if (bTrade && nRecordNum > 1)
		nHashCode = std::hash<std::string>()(std::string((char *)&vctData[1], (nRecordNum - 1)* sizeof(KLineData)));
	else if (nRecordNum > 0)
		nHashCode = std::hash<std::string>()(std::string((char *)&vctData[0], (nRecordNum)* sizeof(KLineData)));

	return nHashCode;
}

unsigned int CKLineData::getCollectTime()
{
	return nCollectTime;
}

std::string CKLineData::FormatData()
{
	if (vctData.size() == 0)
		return "";
	std::stringstream str;
	str << "Date,Open,High,Low,Close,Volumn,Amount,Position;" << std::endl;
	for (int i = 0; i < nRecordNum;i++)
	{
		str << vctData[i].date << ","
			<< vctData[i].open << ","
			<< vctData[i].high << ","
			<< vctData[i].low << ","
			<< vctData[i].close << ","
			<< vctData[i].volume << ","
			<< vctData[i].amount << ","
			<< vctData[i].position << ";"<<std::endl;
	}
	return str.str();
}

bool CKLineData::Compare(CKLineData& dataOther, int bTime, std::function<bool(const std::string &, const site_lua& , const site_lua& , const KLineData&, const KLineData&)> funcomapre)
{
	if (nRecordNum != dataOther.nRecordNum 
		|| vctData.size() < nRecordNum || dataOther.vctData.size()< dataOther.nRecordNum)
		return false;

	int i = 0;
	if (bTime)
		i = 1;

	site_lua siteNormal{ strSign, strIP, nPort };
	site_lua site2{ dataOther.strSign, dataOther.strIP, dataOther.nPort };
	for (; i < nRecordNum; i++)
	{
		KLineData& k1 = vctData[i];
		KLineData& k2 = dataOther.vctData[i];

		if (funcomapre)
		{
			if (!funcomapre(strCode, siteNormal, site2, k1, k2)){
// 				CLogSubject::Instance()->WriteLog(LOG_MESSAGE_ALL, LogLvlWarnning
// 					, "Data compare failed,[%s_%s:%d],code:%s,date:%d"
// 					, site2.sign.c_str(), site2.ip.c_str(), site2.port, strCode.c_str(), k2.date);
				return false;
			}
		}
		else if (!k1.Compare(k2))
			return false;
	}
	return true;

}
